#include "sizing_operations.h"
#include "mainwindow.h"
#include "resource.h"

using namespace std;


ResizeSettings resizeSettings;

RotateSettings rotateSettings;

/*
	Naam: Mark van der Steen
*/
#pragma region rotate
BOOL CALLBACK rotateSetupProc(
	HWND hWndDialog,
	UINT message,
	WPARAM wParam,
	LPARAM lParam )
{
	switch( message )
	{
	case WM_INITDIALOG:
		//default waarde van invoerveld
		SetDlgItemText( hWndDialog, IDC_ROTATE, L"1" );
		break;
	
	case WM_COMMAND:
		switch( LOWORD( wParam ) )
		{
		case IDOK:
			{
				//de ingevoerde waardes lezen en ze opslaan in rotateSettings.
				wchar_t szDegrees[32];

				if( GetDlgItemText( hWndDialog, IDC_ROTATE, szDegrees, 32 ) == 0 )
				{
					//Als het invoerveld leeg is een waarschuwing geven
					MessageBox( hWndDialog, L"Invalid input", L"Message", MB_OK | MB_ICONEXCLAMATION );
					return FALSE;
				}

				// Wanneer er text in het invoerveld wordt ingevuld zorgt _wtoi ervoor dat de waarde op 0 wordt gezet
				rotateSettings._degrees = _wtoi( szDegrees );
				
			}
		case IDCANCEL:
			EndDialog( hWndDialog, wParam );
			return TRUE;
		}
		break;
	}
	return FALSE;
}

/*
	Naam: Mark van der Steen
*/
bool rotateSetup( Task *task )
{
	MainWindow *mainWindow = MainWindow::instance();
	if( DialogBox(
		mainWindow->hInstance(),
		MAKEINTRESOURCE( IDD_DIALOG_ROTATE ),
		mainWindow->hWnd(),
		(DLGPROC) &rotateSetupProc ) == IDOK )
	{
		//Allocate a structure and copy the settings. This structure is used in
		//the rotate method.
		task->_data = new RotateSettings( rotateSettings );
		// Grootte van orginele plaatje achterhalen
		Image test = task->_originalImage->getImage();
		unsigned int h = test.height();
		unsigned int w = test.width();

		// Nieuwe plaatje creeeren
		task->createDestination(w,h);
		return true;
	}
	return false; //The user cancelled the operation.
}

/*
	Naam: Mark van der Steen
*/
bool rotate( Image &imgSource, Image &imgResult, void *data )
{
	// variabelen declareren
	double degrees;
	unsigned int row, col, channel, lastBit, lastCol;
	degrees = ( (RotateSettings*) data )->_degrees;
	// Berekening van waarde PI
	const double PI = 4.0*atan(1.0);
	// Omzetten van graden naar radialen
	double radialen = degrees / (180/PI);
	// cosinus uitrekenen
	double cos_rot = cos(-radialen);
	// sinus uitrekenen
	double sin_rot = sin(-radialen);
	// Berekenen van 0 punt (middelpunt) bronplaatje
	double oldcenx = imgSource.width() / 2;
	double oldceny = imgSource.height() / 2;
	// Berekenen van 0 punt (middelpunt) nieuwe plaatje
	double newcenx = imgResult.width() / 2;
	double newceny = imgResult.height() / 2;

	// Bij 0 of 360 graden rotatie bronplaatje copieeren naar nieuwe plaatje
	if ((degrees == 0)|(degrees == 360)|(degrees == -360)){
		imgResult = imgSource;
	}
	// Code om image 90 graden te roteren zonder algorithme
	if ((degrees == 90)|(degrees == -270))
	{
			for( row = 0; row < imgSource.height(); row++ )
			{
				// -1 omdat er gerekend wordt vanaf 0
				lastBit = (imgSource.height()-1); 
				// loop door de kolommen
				for( col = 0; col < imgSource.width(); col++, lastBit-- )
					// loop door de channels
					for( channel = 0; channel < 3; channel++ )
						// spiegel de bitjes in de source afbeelding
						imgResult[row][lastBit][channel] = imgSource[col][row][channel];
			}
	}
	// Code om image -90 graden te roteren zonder algorithme
	if ((degrees == -90)|(degrees == 270))
	{	
			for( row = 0; row < imgSource.height(); row++ )
			{
				// -1 omdat er gerekend wordt vanaf 0
				lastBit = (imgSource.height()-1); 
				// loop door de kolommen
				for( col = 0; col < imgSource.width(); col++, lastBit-- )
					// loop door de channels
					for( channel = 0; channel < 3; channel++ )
						// spiegel de bitjes in de source afbeelding
						imgResult[lastBit][col][channel] = imgSource[col][row][channel];
			}
	}
	// Code om image 180 graden te roteren zonder algorithme
	if ((degrees == 180)|(degrees == -180))
	{
			lastCol = (imgSource.width()-1);
			for( row = 0; row < imgSource.height(); row++, lastCol-- )
			{
				// -1 omdat er gerekend wordt vanaf 0
				lastBit = (imgSource.height()-1); 
				// loop door de kolommen
				for( col = 0; col < imgSource.width(); col++, lastBit-- )
					// loop door de channels
					for( channel = 0; channel < 3; channel++ )
						// spiegel de bitjes in de source afbeelding
						imgResult[lastBit][lastCol][channel] = imgSource[col][row][channel];
			}
	}
	else{
	for( col = 0; col < imgResult.width(); ++col)
			{		
				for( row = 0; row < imgResult.height(); ++row)
					{
						for( channel = 0; channel < 3; ++channel )
							{	
								// Berekening van: x coordinaat - nulpunt van source image
								double dx = col- oldcenx;
								// Berekening van: y coordinaat - nulpunt van source image
								double dy = row - oldceny;
								// Berekening waar de pixel vandaan komt uit de source image voor de x coordinaat
								unsigned int transx = unsigned int( (cos_rot * dx) - (sin_rot * dy) + oldcenx);
								// Berekening waar de pixel vandaan komt uit de source image voor de y coordinaat
								unsigned int transy = unsigned int( (sin_rot * dx) + (cos_rot * dy) + oldceny);
								// Als de pixel niet voorkomt in de source image dan de pixel gewoon wit laten
								if((transx < imgSource.width())&&(transy < imgSource.height())){
									// Pixel uit source image naar nieuwe image copieren
									imgResult[ row ][ col ][ channel ] = imgSource[ transy ][ transx ][ channel ];
								}								
							}
					}
			}
	}
	delete data;
	return true;
}

// Gemaakt door Bas Janssens
BOOL CALLBACK resizeSetupProc(
	HWND hWndDialog,
	UINT message,
	WPARAM wParam,
    LPARAM lParam
	)
{
	switch( message )
	{
	case WM_INITDIALOG:
		//Initialiseren van het dialoogvenster:
		SendMessage(
			GetDlgItem(hWndDialog, IDC_SLIDER_SIZE),
			TBM_SETRANGE, TRUE, MAKELONG(50, 200));
		SendMessage(
			GetDlgItem(hWndDialog, IDC_SLIDER_SIZE),
			TBM_SETPOS, TRUE, (LONG) 100);
			
		//Schaal tekstveld innstellen 100%
        SetDlgItemText( hWndDialog, IDC_EDIT_SCALE, L"100" );
		
		break;
	case WM_COMMAND:
		switch( LOWORD( wParam ) )
		{
		case IDOK:
			{
				//Waardes uit het veld lezen en opslaan in resizeSettings.
				wchar_t strResize[32];

				//Geen van de velden mag leeg zijn				
				if( GetDlgItemText( hWndDialog, IDC_EDIT_SCALE, strResize, 32 ) == 0)
				{
					MessageBox( hWndDialog, L"Voer waarden in of verschuif de slider!", L"Waarschuwing", MB_OK | MB_ICONEXCLAMATION );
					return FALSE;
				}

				// De Radiobutton wordt uitgelezen en de mode wordt gezet 
				if(IsDlgButtonChecked( hWndDialog, IDC_RADIO_NEAREST))
				{
					int mode = 0;
					resizeSettings._mode = mode;
				}
				if(IsDlgButtonChecked( hWndDialog, IDC_RADIO_LINEAR))
				{
					int mode = 1;
					resizeSettings._mode = mode;
				}
				if (IsDlgButtonChecked( hWndDialog, IDC_RADIO_BICUBIC))
				{
					int mode = 2;
					resizeSettings._mode = mode;				
				}
				resizeSettings._strResize =  _wtof( strResize ) / 100;
			}
		case IDCANCEL:
			EndDialog( hWndDialog, wParam );
			return TRUE;
		case IDC_EDIT_SCALE:
			if(HIWORD(wParam) == EN_CHANGE){
				wchar_t buf[5];
				GetDlgItemText( hWndDialog, IDC_EDIT_SCALE, buf, 5);
				SendMessage(
					GetDlgItem( hWndDialog, IDC_SLIDER_SIZE ),
					TBM_SETPOS, TRUE, (LONG) _wtoi(buf));

		}
		break;
	}
	break;
	case WM_HSCROLL:
		if((HWND) lParam ==
			GetDlgItem( hWndDialog, IDC_SLIDER_SIZE ))
		{
			//De WM_HSCROLL message wordt verzonden door de slider
			if(LOWORD(wParam) == SB_THUMBPOSITION || LOWORD(wParam) == SB_THUMBTRACK)
			{
				//Positie is veranderd
				wchar_t buf[5];
				_itow((short) HIWORD(wParam), buf, 10);
				SetDlgItemText(hWndDialog, IDC_EDIT_SCALE, buf);
			}
		}
		break;
	}
	return FALSE;
}
//Gemaakt door Jereoen Heugten
//Aangepast door Bas Janssens
bool resizeSetup( Task *task )
{
	MainWindow *mainWindow = MainWindow::instance();
	if( DialogBox(
		mainWindow->hInstance(),
		MAKEINTRESOURCE( IDD_DIALOG_RESIZE ),
		mainWindow->hWnd(),
		(DLGPROC) &resizeSetupProc ) == IDOK )
	{
		//Allocate a structure and copy the settings. This structure is used in
		//the resize method.
		task->_data = new ResizeSettings( resizeSettings );

		//De originele afbeelding ophalen
		Image imgSource = task->_originalImage->getImage();

		//De hoogte en breedte van de originele afbeelding bepalen
		double height = (double) imgSource.height();
		double width = (double) imgSource.width();

		double destHeight = height*resizeSettings._strResize;
		double destWidth = width*resizeSettings._strResize;

		//Nieuw Destination Image (Raamwerk) aanmaken aan de hand van bovenstaande gegevens
		task->createDestination( (int) destWidth, (int) destHeight);

		return true;
	}

	return false; //The user cancelled the operation.

} 
//Gemaakt door Bas Janssens
bool resize( Image &imgSource, Image &imgResult, void *data )
{
	int mode;
	//de mode in de ResizeSettings wordt uitgelezen en in mode geplaatst
	mode = ( (ResizeSettings*) data )->_mode;

	if (mode == 0)
	{
		// Nearest Neighbour.
		//Hier wordt het resultaat gedeeld door het origineel, om de schaalfactor te bepalen.
		//de scale resizesetting wordt niet gebruikt. Bereken hem zelf voor als we hoogte en breedte apart willen scalen
		double Yscale = (double)imgResult.height()/(double)imgSource.height();
		double Xscale = (double)imgResult.width()/(double)imgSource.width();


		for( unsigned int row = 0; row < imgResult.height(); row++ )
			{
				int Srow = (int)floor((row/Yscale));
				

				for( unsigned int col = 0; col < imgResult.width(); col++ )
				{
					int Scol = (int)floor((col/Xscale));
					

					for( unsigned int channel = 0; channel < 3; channel++ )
					{
						imgResult[ row ][ col ][ channel ] = imgSource[Srow] [Scol] [channel];
								
					}
				}
		}

	}
	else if ( mode == 1)
	{
		// Linear
		//De result grootte wordt gedeeld door de grootte van het origineel
		double Yscale = ((double)imgResult.height()/(double)imgSource.height());
		double Xscale = ((double)imgResult.width()/(double)imgSource.width());
		int Srow = 0;
		int Scol = 0;

		for( unsigned int row = 0; row < imgResult.height(); row++)
			{
				//op row niveau nog steeds NearestNeighbour. ooit bilinear willen maken dan row net zo uitrekenen als col
				unsigned int Srow = (int)floor((row/Xscale));
			
				for( unsigned int col = 0; col < imgResult.width(); col++ )
				{
					//voor linear moet je origineel delen door result vandaar 1 /(dus omgekeerd)
					// eersteKin is de 0,71 uit de sheet blz 13
					double eersteKin = 1/Xscale;

					//Kin is kleur index van de pixel
					double Kin = col*eersteKin;
					double achterdekomma = Kin - floor(Kin);
					
					//Als de waarde achter de komma groter is dan de eerstKin de Kin naar boven afronden
					if (achterdekomma >= eersteKin)
					{
						int Scol = (int)ceil(Kin);
						for( unsigned int channel = 0; channel < 3; ++channel )
						{
							imgResult[ row ][ col ][ channel ] = imgSource[Srow] [Scol] [channel];
						}
					}
					//voor als de de Kin een heel getal is
					else if(Kin == floor(Kin))
					{
						int Scol = (int)(Kin);
						for( unsigned int channel = 0; channel < 3; ++channel )
						{
							imgResult[ row ][ col ][ channel ] = imgSource[Srow] [Scol] [channel];
						}
					}
					//hier wordt de lineare berekening gedaan eerst de kleur bepalen en dan maal een kommagetal
					else
					{
						for( unsigned int channel = 0; channel < 3; channel++ )
						{
							unsigned int Scol1 = (unsigned int)floor(Kin);
							unsigned int Scol2 = (unsigned int)ceil(Kin);
							//berekening om de kleur te bepalen. zie ook blz 13 van de sheets blok 42006.ppt kolom lineare interpolatie
							imgResult[ row ][ col ][ channel ] = (byte)(((double)imgSource[Srow] [Scol1] [channel] * (eersteKin - achterdekomma)) + ((double)imgSource[Srow] [Scol2] [channel] * achterdekomma));
						}
					}
				}
		}

	}
	else if (mode == 2)
	{
		// Bicubic
		//scale bepalen
		double dy = (double)imgSource.height()/(double)imgResult.height();
		double dx = (double)imgSource.width()/(double)imgResult.width();

		int t[4];
		//Door de imgResult lopen
		for( unsigned int row = 0; row < imgResult.height(); row++ )
			{
				double Srow = row * dy;

				for( unsigned int col = 0; col < imgResult.width(); col++ )
				{
					double Scol = col * dx;

					for( unsigned int channel = 0; channel < 3; ++channel )
					{
						//Bereken gedeelte achter de komma
						double ScolFrac = Scol - floor(Scol);
						double SrowFrac = Srow - floor(Srow);

						//Bereken gedeelte voor de komman (altijd afgerond naar beneden)
						unsigned int ScolWhole = (unsigned int)floor(Scol);
						unsigned int SrowWhole = (unsigned int)floor(Srow);

						unsigned int a = 0;
						unsigned int b = 0;
						unsigned int c = 0;
						unsigned int d = 0;
						
						//Zero padding en het berekennen van de T waarde die later aan de cubic functie worden meegegeven
						if (SrowWhole >= 1)
						{
							a = 0;
							b = 0;
							c = 0;
							d = 0;

							if (ScolWhole >= 1)
							{
								a = imgSource[ SrowWhole - 1] [ ScolWhole - 1 ] [ channel ];
							}
							b = imgSource[ SrowWhole - 1] [ ScolWhole ] [ channel ];
							if (ScolWhole <= imgSource.width() - 1)
							{
								c = imgSource[ SrowWhole - 1] [ ScolWhole + 1 ] [ channel ];
							}
							if (ScolWhole <= imgSource.width() - 2)
							{
								d = imgSource[ SrowWhole - 1] [ ScolWhole + 2 ] [ channel ];
							}

							t[0] = cubic(a, b, c, d, ScolFrac);
						}
						else
						{
							t[0] = 0;
						}

						a = 0;
						b = 0;
						c = 0;
						d = 0;

						if (ScolWhole >= 1)
						{
							a = imgSource[ SrowWhole] [ ScolWhole - 1 ] [ channel ];
						}
						b = imgSource[ SrowWhole] [ ScolWhole ] [ channel ];
						if (ScolWhole <= imgSource.width() - 1)
						{
							c = imgSource[ SrowWhole] [ ScolWhole + 1 ] [ channel ];
						}
						if (ScolWhole <= imgSource.width() - 2)
						{
							d = imgSource[ SrowWhole] [ ScolWhole + 2 ] [ channel ];
						}

						t[1] = cubic(a, b, c, d, ScolFrac);

						if (SrowWhole <= imgSource.height() - 1)
						{
							a = 0;
							b = 0;
							c = 0;
							d = 0;

							if (ScolWhole >= 1)
							{
								a = imgSource[ SrowWhole + 1] [ ScolWhole - 1 ] [ channel ];
							}
							b = imgSource[ SrowWhole + 1] [ ScolWhole ] [ channel ];
							if (ScolWhole <= imgSource.width() - 1)
							{
								c = imgSource[ SrowWhole + 1] [ ScolWhole + 1 ] [ channel ];
							}
							if (ScolWhole <= imgSource.width() - 2)
							{
								d = imgSource[ SrowWhole + 1] [ ScolWhole + 2 ] [ channel ];
							}

							t[2] = cubic(a, b, c, d, ScolFrac);
						}
						else
						{
							t[2] = 0;
						}
						if (SrowWhole <= imgSource.height() - 2)
						{
							a = 0;
							b = 0;
							c = 0;
							d = 0;

							if (ScolWhole >= 1)
							{
								a = imgSource[ SrowWhole + 2] [ ScolWhole - 1 ] [ channel ];
							}
							b = imgSource[ SrowWhole + 2] [ ScolWhole ] [ channel ];
							if (ScolWhole <= imgSource.width() - 1)
							{
								c = imgSource[ SrowWhole + 2] [ ScolWhole + 1 ] [ channel ];
							}
							if (ScolWhole <= imgSource.width() - 2)
							{
								d = imgSource[ SrowWhole + 2] [ ScolWhole + 2 ] [ channel ];
							}

							t[3] = cubic(a, b, c, d, ScolFrac);
						}
						else
						{
							t[3] = 0;
						} 

						imgResult[ row ] [ col ] [ channel ] = cubic(t[0], t[1], t[2], t[3], SrowFrac);
					}
				}
		}

	}
	delete data;
	return true;
}
//De standaard berekening voor de bicubic interpolatie
int cubic(int v0, int v1, int v2, int v3, double t)
{
	int p = (v3 - v2) - (v0 - v1);
	int q = (v0 - v1) - p;
	int r = v2 - v0;
	int s = v1;
	
	double temp = ((double)p * (t * t * t)) + ((double)q * (t * t)) + ((double)r * t) + (double)s;
	return (int)temp;
}
//De standaard berekening voor de bicubic interpolatie
int cubic2(int v0, int v1, int v2, int v3, double t)
{
	double temp = (((( -7 * v0 + 21 * v1 - 21 * v2 + 7 * v3) * t + (15 * v0 - 36 * v1 + 27 * v2 - 6 * v3)) * t + (-9 * v0 + 9 * v2)) * t + (v0 +16 * v1 + v2)) / 18.0;

	return (int)temp;
}